package com.ar4j.test.domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import com.ar4j.ActiveRecordContext;
import com.ar4j.ActiveRecordFactory;
import com.ar4j.sql.INamedQuerySource;
import com.ar4j.type.DateWithoutMillis;

/**
 * Various examples from the user guide
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext-junit.xml" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
@Transactional
public class Examples {
  
  @Autowired
  private DataSource dataSource;
  
  @Autowired
  private PlatformTransactionManager txManager;
  
  @Autowired
  private INamedQuerySource querySource;
  
  @Test
  public void testGiveMeMyInstanceDamnit() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    System.out.println("Wohoo, and instance: " + instance);
  }
  
  @Test
  public void testWhyDoICareAboutThis() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    System.out.println("Who am I really?: " + instance.getBaseClass());
  }
  
  @Test
  public void testOooohProperties() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    
    // what do we have?
    Set<String> propertyNames = instance.getPropertyNames();
    for(String property : propertyNames) {
      System.out.println("I have a property called '" + property + "'");
      
      Class<?> type = instance.getPropertyType(property);
      System.out.println("It is of type: " + type);
      
    }
    
    // are you sure this is in stock?
    System.out.println("Does it have an ID?: " + instance.hasProperty("id"));
    System.out.println("What about something I made up?: " + instance.hasProperty("someUnknownProperty"));
    
  }
  
  @Test
  public void testWhatAboutTypeSafety() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);

    instance.setCode("FIRST!!!");
    instance.setDescriptionValue("Best instance...EVER");
    
    System.out.println("Look ma, no getter, code is: " + instance.getProperty("code"));
    System.out.println("Look ma, no getter or type safety, descriptionValue is: " + instance.getProperty("descriptionValue", String.class));
    
    instance.setProperty("code", "NO LONGER FIRST :(");
    instance.setProperty("descriptionValue", "i have overriden you");
    
    System.out.println("Someone has fiddled with my properties, code is: " + instance.getCode());
    System.out.println("Someone has fiddled with my properties, descriptionValue is: " + instance.getDescriptionValue());
    
    for(Map.Entry<String, Object> properties : instance.getPropertyMap().entrySet()) {
      System.out.println("Full dump!!! Property '" + properties.getKey() + "' = " + properties.getValue());
    }
        
  }
  
  @Test
  public void testBackToBasics() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    
    // set up first instance
    Primary firstInstance = factory.getActiveRecord(Primary.class);
    firstInstance.setId(new Long(1));
    firstInstance.setCode("FIRST");
    
    // set up second instance
    Primary secondInstance = factory.getActiveRecord(Primary.class);
    secondInstance.setId(new Long(2));
    secondInstance.setCode("FIRST"); // not really, but bear with us
    
    System.out.println("Is our first instance equal to itself, it better be: " + firstInstance.equals(firstInstance));
    System.out.println("Is our first instance equal to the second, it better NOT be: " + firstInstance.equals(secondInstance));
    System.out.println("Do the first and second instance hold the same content, I hope so: " + firstInstance.isSameContent(secondInstance));
    
    System.out.println("The first hashcode is: " + firstInstance.hashCode());
    System.out.println("Calling it twice better give us the same result: " + firstInstance.hashCode());
    System.out.println("The second hashcode is, better be different than the first: " + secondInstance.hashCode());
    
    System.out.println("Can we pretty print our first instance: " + firstInstance.toString());
    
    // i hope these don't start a "clone" army - zing...
    Primary cloneOfFirst = firstInstance.cloneRecord();
    System.out.println("I am not my clone, right? " + !firstInstance.equals(cloneOfFirst));
    System.out.println("But I hold the same data as my clone: " + firstInstance.isSameContent(cloneOfFirst));
        
  }
  
  @Test
  public void testWhereAmIGoing() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.setId(new Long(20));
    
    System.out.println("My table is: " + instance.getTableName());
    System.out.println("I am identified by the property: " + instance.getIdentifierPropertyName());
    System.out.println("My identifier has the type of: " + instance.getIdentifierPropertyType());
    System.out.println("My identifier has the value of: " + instance.getIdentifier());
    System.out.println("My 'enumField' needs to be converted by: " + instance.getPropertyTypeConverter("enumField"));
    System.out.println("My 'code' does not needs to be converted, so i should get null: " + instance.getPropertyTypeConverter("code"));
  }
  
  @Test
  public void testContextShmantex() {
    ActiveRecordContext defaultContext = new ActiveRecordContext();
    defaultContext.setDataSource(dataSource);
    defaultContext.setPlatformTransactionManager(txManager);
    defaultContext.setNamedQuerySource(querySource);
    
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    
    System.out.println("All context values from new instance should be null: " + instance.getContext());
    
    instance.getContext().setValuesFrom(defaultContext);
    
    System.out.println("We should have gotten a data source from the default: " + instance.getContext().getDataSource());
    System.out.println("We should have gotten a txManager from the default: " + instance.getContext().getPlatformTransactionManager());
    System.out.println("We should have gotten a query source from the default: " + instance.getContext().getNamedQuerySource());
    System.out.println("We should have gotten a SimpleJdbc template from the default: " + instance.getContext().getSimpleTemplate());

  }
  
  @Test
  public void testDoIHaveAPlaceToLive() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    String query = "SELECT COUNT(*) FROM information_schema.TABLES";
    int results = instance.getContext().getSimpleTemplate().queryForObject(query, Integer.class);
    
    System.out.println("How many tables in my data source: " + results);
  }
  
  @Test
  public void testOneHundredGoldPiecesToTheManWhoFindsMeRecordsISeek() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    Map<String, Object> firstParameters = new HashMap<String, Object>();
    firstParameters.put("code", "FIRST_PRIMARY");
    Collection<Primary> firstResults = instance.find(firstParameters);
    
    System.out.println("\nCode lookup got us some records: " + firstResults.size());
    for(Primary result : firstResults) {
      System.out.println("One of our records is: " + result);
    }
    
    Map<String, Object> secondParameters = new HashMap<String, Object>();
    secondParameters.put("code", "FIRST_PRIMARY");
    secondParameters.put("enumField", TestEnum.FIRST);
    Collection<Primary> secondResults = instance.find(secondParameters);
    
    System.out.println("\nCode and enum lookup got us some records: " + secondResults.size());
    for(Primary result : secondResults) {
      System.out.println("One of our records is: " + result);
    }
    
    Map<String, Object> thirdParameters = new HashMap<String, Object>();
    thirdParameters.put("code", "I SHOULD NOT BE");
    Collection<Primary> fourthResults = instance.find(thirdParameters);
    System.out.println("\nNon existing code lookup should have given us zero records: " + fourthResults.size());
    
  }
  
  @Test
  public void testThisDataIWantToKeepForever() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    instance.setCode("MY_NEW_RECORD");
    instance.setDescriptionValue("New record we want to create");
    instance.setLongField(123L);
    instance.setDoubleField(123.45);
    instance.setStringField("Some text goes here...maybe even Lorem Ipsum");
    instance.setEnumField(TestEnum.THIRD);
    instance.setDateField(new DateWithoutMillis());
    
    instance.save();
    
    System.out.println("Our new record should have gotten an ID: " + instance.getId());
    
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("id", instance.getId());
    Collection<Primary> loadedRecords = instance.find(parameters);
    
    System.out.println("Should have found one record in the DB with our ID: " + loadedRecords.size());
    System.out.println("Loaded record should be the same as ours: " + (instance.equals(loadedRecords.iterator().next())));
    
    instance.setDescriptionValue("U have updated me...lol");
    instance.save();
    
    loadedRecords = instance.find(parameters);
    
    System.out.println("Should have found one record in the DB with our ID: " + loadedRecords.size());
    System.out.println("Loaded record have the updated description: " + (loadedRecords.iterator().next().getDescriptionValue()));
    
  }
  
  @Test
  public void testThisDataIDontLoveAnymore() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("code", "FIRST_PRIMARY");
    
    Collection<Primary> firstResults = instance.find(parameters);
    System.out.println("Should have gotten one record: " + firstResults.size());
    
    for(Primary result : firstResults) {
      System.out.println("Did we delete? " + result.delete());
    }
    
    Collection<Primary> secondResults = instance.find(parameters);
    System.out.println("Should have gotten no records: " + secondResults.size());
    
    Collection<Primary> allRows = instance.find(new HashMap<String, Object>());
    System.out.println("How many records we have left: " + allRows.size());
    
    instance.delete(new HashMap<String, Object>());
    
    allRows = instance.find(new HashMap<String, Object>());
    System.out.println("And now they are all gone :( - " + allRows.size());
    
  }
  
  @Test
  public void testThisLittlePiggyWhenToMarket() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    long allCount = instance.countAll();
    System.out.println("I have this many records: " + allCount);
    
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("code", "FIRST_PRIMARY");
    long codeCount = instance.count(parameters);
    System.out.println("I have this many records with my code: " + codeCount);
    
  }
  
  @Test
  public void testAreYouReallyThere() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("code", "FIRST_PRIMARY");
    boolean codeExists = instance.exists(parameters);
    System.out.println("It should be there: " + codeExists);
    
    Collection<Long> identifiers = new ArrayList<Long>();
    identifiers.add(1L);
    identifiers.add(2L);
    boolean collectionExists = instance.existsByIdentifier(identifiers);
    System.out.println("The two records should exist: " + collectionExists);
    
    identifiers.add(3L);
    boolean collectionExists2 = instance.existsByIdentifier(identifiers);
    System.out.println("But a third one should NOT exist: " + collectionExists2);
    
    boolean singleExists = instance.existsByIdentifier(1L);
    boolean singleExists2 = instance.existsByIdentifier(999999L);
    System.out.println("Record with ID 1 should be there: " + singleExists);
    System.out.println("Record with fake ID should be there: " + singleExists2);
   
    System.out.println("Our instnace should be new: " + instance.isNewRecord());
    instance.setId(1L);
    System.out.println("But if we set ID to 1 it will not be new: " + instance.isNewRecord());
    instance.setId(99999L);
    System.out.println("But if we set ID to 99999 it will new again: " + instance.isNewRecord());
    
  }
  
  @Test
  public void testOhRecordWhereArtThou() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    Primary loadedById = instance.findByIdentifier(1L);
    System.out.println("Found by ID = 1: " + loadedById);
    
    Collection<Long> ids = Arrays.asList(new Long[] { 1L, 2L });
    Collection<Primary> loadedByIds = instance.findAllByIdentifiers(ids);
    System.out.println("Found " + loadedByIds.size() + " records by ids");
    
    Collection<Primary> loadedAll = instance.findAll();
    System.out.println("Found " + loadedAll.size() + " in the RDBMS");
    
    loadedById.setCode("I HAVE BEEN MADE DIRTY");
    System.out.println("Code before reload: " + loadedById.getCode());
    loadedById.reload();
    System.out.println("Code after reload: " + loadedById.getCode());
    
  }
  
  @Test
  public void testOutDamnSpotOutISay() {
    ActiveRecordFactory factory = ActiveRecordFactory.getFactory();
    Primary instance = factory.getActiveRecord(Primary.class);
    instance.getContext().setDataSource(dataSource);
    
    System.out.println("Do we have a record with ID = 1: " + instance.existsByIdentifier(1L));
    instance.deleteByIdentifier(1L);
    System.out.println("What about now?: " + instance.existsByIdentifier(1L));
    
    Collection<Long> ids = Arrays.asList(new Long[] { 1L, 2L });
    System.out.println("Do we have a record with ID = 2: " + instance.existsByIdentifier(2L));
    instance.deleteByIdentifier(ids);
    System.out.println("What about now?: " + instance.existsByIdentifier(2L));
    
  }
    
}
